#include "udef.h"
#include "io.h"


struct ufs1_dinode di;

/* 
 * funkce prevzata a upravena z dumpafs.c 
 * vypise rozsah nastavenych bitu (pouzitych inodu, volnych bloku)
 */
void
pbits(void *vp, int max)
{
    int i;
    char *p; 
    int count, j;

    for (count = i = 0, p = vp; i < max; i++)
        if (isset(p, i)) {
            if (count)
                printf(",%s", count % 6 ? " " : "\n\t");
            count++;
            printf("%d", i); 
            j = i;
            while ((i+1)<max && isset(p, i+1))
                i++;
            if (i != j)
                printf("-%d", i); 
        }
    printf("\n");
}

/*
 * vypise rozsahy pouzitych bloku dat z db a ib 
 */
void
print_dbib_range(int *pb, int max)
{
	int i, j;
	int count;
	
	for (count = i = 0; i < max && pb[i] != 0; i++) {
			if (count)
				printf(",%s", count % 6 ? " " : "\n\t");
			
			count++;
			printf("%d", pb[i]);
			j = i;
			while ((i+1) < max && ((pb[i]+8) == pb[i+1]))
				i++;
				
			if (i != j)
				printf("-%d", pb[i]); 
	}

    printf("\n");
}

/*
 * vypise nektere info o sblocku
 */
void
print_sblock(void) {
	
	printf("=== Super block ===\n");
	printf("SBLOCK=%d\n", (uint) SBLOCK);
	printf("DEV_BSIZE=%d\n", (uint) DEV_BSIZE);
	
	printf("ADR #%d\n", (uint) (SBLOCK * DEV_BSIZE));	
	
	printf("id=%x%x\n", afs.fs_id[0], afs.fs_id[1]);
	printf("magic: %08x\n", afs.fs_magic);
	printf("rps=%d\n", afs.fs_rps);
	printf("fs_bsize=%d\n", afs.fs_bsize);
	printf("fs_sblkno=%d\n", afs.fs_sblkno);
	printf("fs_cblkno=%d\n", afs.fs_cblkno);	
	printf("fs_iblkno=%d\n", afs.fs_iblkno);
	printf("fs_dblkno=%d\n", afs.fs_dblkno);
	printf("ncg = %d, fpg = %d ipg = %d\n", afs.fs_ncg, afs.fs_fpg, afs.fs_ipg);
	printf("fs_fragshift = %d\n", afs.fs_fragshift);
	
	printf("NINDIR = %d\n", NINDIR(&afs));

	printf("cgsblock: %d\n", (uint) cgsblock(&afs, 0));
	printf("fsbtodb: %d\n", (uint) fsbtodb(&afs, cgsblock(&afs, 0)));
	printf("inodes at: %d\n", (uint) fsbtodb(&afs, cgimin(&afs, 0)));
	printf("data at: %d\n", (uint) fsbtodb(&afs, cgdmin(&afs, 0)));
}

/*
 * vipise nektere info o cg 
 */

int
print_cgblock(struct cg *acg, int ncyl)
{	
	printf("\n=== Cylinder group ===\n");
	printf("Cylinder: %d\n", ncyl);
	
	printf("cgbase %d\n" , (uint) cgbase(&afs, ncyl));
	printf("cgdmin %d\n" , (uint) cgdmin(&afs, ncyl));
	printf("cgimin %d\n" , (uint) cgimin(&afs, ncyl));
	printf("cgsblock %d\n" , (uint) cgsblock(&afs, ncyl));
	printf("cgtod %d\n" , (uint) cgtod(&afs, ncyl));
	printf("cgstart %d\n" , (uint) cgstart(&afs, ncyl));
	
	printf("cgsize=%d\n", (uint) CGSIZE(&afs));
	printf("cg_magic: 0x%X\n", acg->cg_magic);
	printf("cg_niblk: %d\n", acg->cg_niblk);
	printf("cg_ndblk: %d\n", acg->cg_ndblk);
	printf("cg_iusedoff %d (#%d)\n", acg->cg_iusedoff, acg->cg_iusedoff * DEV_BSIZE);
	printf("rotor\t%d\tirotor\t%d\tfrotor\t%d\n",
		acg->cg_rotor, acg->cg_irotor, acg->cg_frotor);
		
	printf("cgimin: %d\n", (uint )cgimin(&afs, ncyl));
	
	printf("csum free block: %d inodes: %d frags: %d\n", 
		(acg->cg_cs).cs_nbfree, (acg->cg_cs).cs_nifree, (acg->cg_cs).cs_nffree);

	
	printf("inodes used:");
	pbits(cg_inosused(acg), afs.fs_ipg);
	
	printf("blks free: ");
	pbits(cg_blksfree(acg), afs.fs_fpg);	

	return 0;
}

/* 
 * metodou puleni naleznu posledni prvek v ib
 */
int
find_last_ib (int *ib)
{
	int point;
	int sub;
	
	point = NINDIRS - 1;
	sub = NINDIRS / 2;
	
	if (ib[point] != 0)
		return point;
	
	point -= sub;
	
	/* jen hrube puleni, kvuli deleni int */
	while (point >= 0 && point < NINDIRS) {
		if (ib[point] != 0) {
			/* bingo */
			if (ib[point+1] == 0)
				return point;
				
			/* jdi zpet */
			sub = (int) (sub / 2);
			point += sub;
		}
		else {
			/* pokracuj dolu */
			sub = (int) (sub / 2);
			point -= sub;
		}
		
		/*
		 * kvuli orezu pri deleni muze nekdy chybet krok do nalezeni pozice
		 * pokud sub == 0 nebo 1, tak dale krokuju po 1
		 */
		if (sub <= 1)
			sub = 2;
	}
	
	return -1;
}

/* 
 * prochazi db a ib a vypisuje odkazy na bloky dat 
 */
int 
print_inodes_ib(GLBVAL *glb, off_t offset) 
{
	int 	st_ib[MAXBSIZE];
	int 	*ib;
	int 	j;
	
	/* vypisu jen prvni a posledni */
	ib = st_ib;
	if (read_fs_block(glb, (void **) &ib, offset)) {
		fprintf(stderr, "Read ib error - print_inodes_ib");
		return 1;
	}
	
	j = find_last_ib(ib);
	if (j >= 0) {
		printf("\t    ib[%d] %d (#%d)\n", 0, ib[0], fsbtodb(&afs, ib[0])*DEV_BSIZE);
		if (j > 1)
			printf("\t    ib[%d] %d (#%d)\n", j, ib[j], fsbtodb(&afs, ib[j])*DEV_BSIZE);
	}
		
	return 0;
}


/*
 * vypise pouzite inody na cg vcetne odkazu db a ib
 */
int 
print_inodes(GLBVAL *glb) 
{
	int 	st_ib1[MAXBSIZE];	/* buffery pro ib a inody */
	int 	st_ib2[MAXBSIZE];
	int 	st_ib3[MAXBSIZE];
	char 	st_ibuf[MAXBSIZE];
	int 	*ib1;				/* ukazatele na buffery */
	int 	*ib2;
	int 	*ib3;
	char 	*ibuf;
	int 	ino;
	int 	j, k;
	ino_t 	ino_start;
	ino_t 	ino_stop;
	struct ufs1_dinode *dp;
	struct cg *acg;
	
	acg = (struct cg *) glb->acg;	
	
	printf("\n=== Inodes ===\n");

	/* prvni / posledni inode */
	ino_start = acg->cg_niblk * acg->cg_cgx;
	ino_stop = ino_start + acg->cg_niblk;

	ibuf = st_ibuf;
	read_inodes_block(glb, (void **) &ibuf, ino_start);
	
	for (ino = ino_start; ino < ino_stop; ino++) {
		
		/* inody ctu po blocich o velikosti INOPB(fs) */
		if (ino_to_fsbo(&afs, ino) == 0) {
			ibuf = st_ibuf;
			read_inodes_block(glb, (void **) &ibuf, ino);
		}
		
		dp = &(((struct ufs1_dinode *)ibuf)[ino_to_fsbo(&afs, ino)]);
		
		/* XXX pouzit bitmapu */
		/* prazdny inode */
		if (dp->di_nlink == 0)
			continue;
		
		printf("%d) ", ino);
		printf("Adr %d ", (uint) ino_to_fsba(&afs, ino));
		printf("di_size: %7d\t", (uint) dp->di_size);
		printf("di_nlink: %d\t", dp->di_nlink);
		printf("di_blocks: %d\t", dp->di_blocks);
	
		printf("\n");		
		
		
		
		for (j = 0; j < NDADDR && dp->di_db[j] != 0; j++)
			printf("\tdi_db[%d] %d (#%d)\n", j, dp->di_db[j], fsbtodb(&afs, dp->di_db[j])*DEV_BSIZE);
		
		for (j = 0; j < NIADDR && dp->di_ib[j] != 0; j++) {
			printf("\t di_ib[%d] %d (#%d)\n", j, dp->di_ib[j], fsbtodb(&afs, dp->di_ib[j])*DEV_BSIZE);
			print_inodes_ib(glb, dp->di_ib[j]);
		}
		
		
		/* direct block */
		printf("\t db: ");
		print_dbib_range(dp->di_db, NDADDR);


		/* 1st indir block */
		if (dp->di_db[NDADDR] != 0) {
			ib1 = st_ib1;
			if (read_fs_block(glb, (void **) &ib1, dp->di_db[NDADDR]))
				warn("Can not read 1st indirect block");
				
			printf("\tib1: ");
			print_dbib_range(ib1, NINDIRS);		
		}
		

		/* 2nd indir block */
		if (dp->di_db[NDADDR+1] != 0) {
			ib1 = st_ib1;
			if (read_fs_block(glb, (void **) &ib1, dp->di_db[NDADDR+1]))
				warn("Can not read 1st indirect block");
				
			j=0;
			while (ib1[j] != 0) {
				printf("\tib2: ");
				ib2 = st_ib2;
				if (read_fs_block(glb, (void **) &ib2, ib1[j]))
					warn("Can not read 2nd indirect block");
					
				print_dbib_range(ib2, NINDIRS);	
				j++;
			}
		}
		
		/* 3rd indirect block */
		if (dp->di_db[NDADDR+2] != 0) {
			ib1 = st_ib1;
			if (read_fs_block(glb, (void **) &ib1, dp->di_db[NDADDR+2]))
				warn("Can not read 1st indirect block");
				
			j=0;
			while (ib1[j] != 0) {
				printf("\tib2: ");
				ib2 = st_ib2;
				if (read_fs_block(glb, (void **) &ib2, ib1[j]))
					warn("Can not read 2nd indirect block");
				
				k=0;
				while (ib2[k] != 0) {
					printf("\tib3: ");
					ib3 = st_ib3;
					if (read_fs_block(glb, (void **) &ib3, ib2[k]))
						warn("Can not read 3rd indirect block");
						
					print_dbib_range(ib3, NINDIRS);	
					k++;
				}
				
				j++;
			}
		}
	}	
	
	return 0;
}
